package com.spotalog.server.service.rest;

import static com.spotalog.shared.service.rest.Constants.DEFAULT_PHOTO_MIME;
import static com.spotalog.shared.service.rest.Constants.PHOTO_INGEST_FIELD;
import static com.spotalog.shared.service.rest.Constants.PHOTO_MIME_FIELD;

import java.util.Collection;
import java.util.Map;

import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;

import lombok.extern.slf4j.Slf4j;

import com.google.common.base.Function;
import com.google.common.base.Objects;
import com.google.common.base.Strings;
import com.google.common.collect.Maps;
import com.google.inject.Provides;
import com.googlecode.objectify.ObjectifyFilter;
import com.spotalog.server.model.ModelModule;
import com.spotalog.server.service.ServiceModule;
import com.spotalog.server.service.rest.exceptions.InvalidParameterExceptionMapper;
import com.spotalog.server.service.rest.exceptions.NotFoundExceptionMapper;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.json.JSONConfiguration;
import com.sun.jersey.guice.JerseyServletModule;
import com.sun.jersey.guice.spi.container.servlet.GuiceContainer;

@Slf4j
public class RestServletModule extends JerseyServletModule {
	@Override
	protected void configureServlets() {
		super.configureServlets();
		install(new ModelModule());
		install(new ServiceModule());
		
		// REQUIRED
		bind(NotFoundExceptionMapper.class);
		// REQUIRED
		bind(InvalidParameterExceptionMapper.class);
		
		// REQUIRED
		bind(SpotResource.class);
		// REQUIRED
		bind(PhotoResource.class);
		// REQUIRED
		bind(JaxbContextResolver.class);
		// REQUIRED
		bind(ObjectifyFilter.class).in(Singleton.class);

		Map<String, String> jerseyParams = Maps.newHashMap();
		jerseyParams.put(PackagesResourceConfig.PROPERTY_PACKAGES, "unbound");
		jerseyParams.put(JSONConfiguration.FEATURE_POJO_MAPPING, "true");
		// Route all requests through Objectify Filter
		filter("/*").through(CORSFilter.class);
		filter("/*").through(ObjectifyFilter.class);
		// Route all requests through GuiceContainer
		serve("/rest/*").with(GuiceContainer.class, jerseyParams);
	}

	@Provides
	@Singleton
	JAXBContext provideJaxbContext() {
		try {
			return JAXBContext.newInstance("com.spotalog.shared.model:com.spotalog.shared.service.rest");
		} catch (JAXBException ex) {
			log.error("{}", ex);
			return null;
		}
	}

	@Provides
	Function<HttpServletRequest, String> provideMimeTypeExtractor() {
		return new Function<HttpServletRequest, String>() {
			@Override
			public String apply(HttpServletRequest input) {
				return Objects.firstNonNull(Strings.emptyToNull(input.getParameter(PHOTO_MIME_FIELD)),
						DEFAULT_PHOTO_MIME);
			}
		};
	}

	@Provides
	Function<Map<String, Collection<String>>, Collection<String>> provideBlobKeyExtractor() {
		return new Function<Map<String, Collection<String>>, Collection<String>>() {
			@Override
			public Collection<String> apply(Map<String, Collection<String>> input) {
				return input.get(PHOTO_INGEST_FIELD);
			}
		};
	}
}
